from datetime import datetime
from urllib.parse import urlparse, parse_qs

from flask import Blueprint, make_response, current_app
from prometheus_client import generate_latest, CONTENT_TYPE_LATEST
from requests import HTTPError

from app.metrics.flow_gateway_metrics import FlowGatewayMetrics
from ... import ts_client
from ...clients.device_client import query_list, fetch_onething_device_status
from ...models.device_info import DeviceInfo
from ...models.user_token import UserToken

metrics_adapter = Blueprint('metrics_adapter', __name__)


@metrics_adapter.get('/refresh/<device_number>')
def refresh_token(device_number: str):
    device_info = query_device(device_number)
    return _refresh_token(device_info)


def _refresh_token(device_info: DeviceInfo):
    device_info = next(iter(query_list(UserToken.from_device_info(device_info), device_info.number)), None)
    if device_info is not None:
        ts_client.save(device_info)
        current_app.logger.info(f"save {device_info.number} device token to bts.")
    return device_info


@metrics_adapter.get('/<device_number>')
def device_metrics(device_number):
    try:
        device_info = query_device(device_number)
        parsed_url = urlparse(device_info.url)
        sign_params = parse_qs(parsed_url.query)
        expire, *_ = sign_params.get('expire')
        if (int(expire) - int(datetime.now().timestamp())) < 300:
            current_app.logger.info('the device token expires in 5 minutes')
            device_info = _refresh_token(device_info)
        metrics = FlowGatewayMetrics()
        fetch_device_metrics(device_info, metrics)
        response = make_response(generate_latest(metrics.registry))
        response.content_type = CONTENT_TYPE_LATEST
        return response
    except (HTTPError, Exception) as ex:
        if isinstance(ex, HTTPError):
            if ex.response.status_code == 404:
                device_info = query_device(device_number)
                _refresh_token(device_info)
        current_app.log_exception(ex)


def query_device(device_number: str):
    device_info = ts_client.query(DeviceInfo.generate_rowkey(device_number), DeviceInfo)
    if device_info is None:
        raise RuntimeError(f"the device {device_number} not found")
    return device_info


def fetch_device_metrics(device_info, metrics):
    metrics_info_list = fetch_onething_device_status(device_info)
    for metrics_info in metrics_info_list:
        metrics.export_uplink_bandwidth_usage(*metrics_info)
        metrics.export_downlink_bandwidth_usage(*metrics_info)
        metrics.export_sent_data_bytes_total(*metrics_info)
        metrics.export_received_data_bytes_total(*metrics_info)
